package org.knight.gearbox.security;

import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.MethodInvocationAdapter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.SimpleMethodInvocation;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.net.Authenticator;

@Aspect
@Component
public class DemoSecurityAspect {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MethodSecurityInterceptor interceptor;

    @Pointcut("  execution(public * org.knight.gearbox.service.*.do*(..))) && !execution(public * org.knight.gearbox.service.*.query*(..)))")
    public void BrokerAspect() {
        //只拦截查询操作以外的方法
    }


    @Around("BrokerAspect()")
    public Object accessControll(ProceedingJoinPoint pjp) {

        AccessAware accessAware = null;
        for (Object arg : pjp.getArgs()) {
            if (arg instanceof AccessAware) {
                accessAware = (AccessAware) arg;
                break;
            }
        }

        accessAware.setUserId(UserContextUtil.getUserId());
        UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(accessAware, "");
        authenticationManager.authenticate(token);

        SecurityContextHolder.getContext().setAuthentication(token);


        SimpleMethodInvocation invocation = new SimpleMethodInvocation(pjp.getTarget(), getMethod(pjp), pjp.getArgs()) {
            @Override
            public Object proceed() {
                try {
                    return pjp.proceed();
                } catch (Throwable t) {
                    throw new RuntimeException(t);
                }
            }
        };

        // interceptor.invoke()
        try {
            return interceptor.invoke(invocation);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    private Method getMethod(ProceedingJoinPoint pjp) {

        String targetMethodName = pjp.getStaticPart().getSignature().getName();

        Class<?>[] types = ((CodeSignature) pjp.getStaticPart().getSignature()).getParameterTypes();

        Class<?> declaredType = pjp.getStaticPart().getSignature().getDeclaringType();

        return findMethod(targetMethodName, declaredType, types);
    }

    private Method findMethod(String name, Class<?> declaringType, Class<?>[] params) {
        Method method = null;
        try {
            method = declaringType.getMethod(name, params);
        } catch (NoSuchMethodException nme) {
            //
        }

        if (method == null) {
            try {
                method = declaringType.getDeclaredMethod(name, params);
            } catch (NoSuchMethodException nme) {
                //
            }
        }
        return method;
    }
}
